<?php
/**
 * Generate SQL select queries.
 *
 */
class Database_Select {
    /**
     * The driver to use.
     * @var Database_Driver
     */
    protected $driver = null;
    /**
     * The parts of the query.
     * @var array
     */
    protected $query = array(
        'distinct'	=> false,
        'from'	    => array(),
        'where'	    => array(),
        'orderby'	=> '',
        'limit'		=> array(
            'rowcount'	=> 0,
            'offset'	=> 0
        )
    );

    /**
     * Constructor. Use the passed in driver or get the default one.
     * @param Database_Driver $driver
     */
    public function __construct(Database_Driver $driver = null) {
        if(null === $driver) {
            $this->driver = Framework::getDefaultDatabase();
        } else {
            $this->driver = $driver;
        }
    }
    
    /**
     * Make the query SELECT DISTINCT
     * @return Database_Select
     */
    public function distinct() {
        $this->query['distinct'] = true;
        return $this;
    }

    /**
     * Add a from clause.
     * @param array $table
     * @param string $columns
     * @return Database_Select
     */
    public function from(array $table, $columns = '*') {
        if(count($table) > 1) {
            throw new Database_Exception('Only one table allowed in from.');
        }
        $this->query['from'][key($table)]['name'] = current($table);
        $columns = (array)$columns;
        foreach($columns as $alias => $column) {
            $this->query['from'][key($table)]['columns'][$alias] = $column;
        }
        return $this;
    }

    /**
     * Add a WHERE clause.
     * @param string $where
     * @param mixed $param
     * @return Database_Select
     */
    public function where($where, $param = false) {
        if($param) {
            $where = str_replace('?', $this->driver->quote($param), $where);
        }
        $this->query['where'][] = $where;
        return $this;
    }
    
    /**
     * Add an ORDER BY clause.
     * @param string|array $order
     * @return Database_Select
     */
    public function orderBy($order) {
        $this->query['orderby'] = $order;
        return $this;
    }
    
    /**
     * Add a LIMIT clause.
     * @param int $rowcount
     * @param int $offset
     * @return Database_Select
     */
    public function limit($rowcount, $offset = 0) {
        $this->query['limit']['rowcount'] = $rowcount;
        $this->query['limit']['offset'] = $offset;
        return $this;
    }
    
    /**
     * Make an SQL query string from the parts.
     * @return string
     */
    public function __toString() {
        $sqlhead = "SELECT ";
        if($this->query['distinct']) {
            $sqlhead .= "DISTINCT ";
        }
        if($this->query['from']) {
            $sqlparts = $this->getFromPart($this->query['from']);
        }
        if(isset($this->query['where'])) {
            $sqltail = " WHERE ";
            $del = '';
            foreach($this->query['where'] as $where) {
                $sqltail .= $del . $where;
                $del = ' AND ';
            }
        }
        if(isset($this->query['orderby'])) {
            $sqltail .= ' ORDER BY ';
            if(is_array($this->query['orderby'])) {
                $sqltail .= implode(', ', $this->query['orderby']);
            } else {
                $sqltail .= $this->query['orderby'];
            }
        }
        if(isset($this->query['limit'])) {
            $sqltail .= ' LIMIT ' . $this->query['limit']['offset'] . ', ' . $this->query['limit']['rowcount'];
        }
        return $sqlhead . implode(', ', $sqlparts['columns']) . ' FROM ' . implode(', ', $sqlparts['from']) . $sqltail;
    }
    
    /**
     * Get the from part.
     * @param array $from
     * @return array
     */
    private function getFromPart($from) {
        $output = array();
        foreach($from as $k => $f) {
            if(is_string($k)) {
                $tablealias = $k;
            } else {
                $tablealias = $f['name'];
            }
            $output['from'][] = $f['name'] . ($f['name'] != $tablealias ? " AS $k" : '');
            foreach($f['columns'] as $a => $column) {
                if($column instanceof Database_Expr) {
                    $output['columns'][] = "$column AS $a";
                } else {
                    if(is_string($a)) {
                        $output['columns'][] = "$tablealias.$column AS $a";
                    } else {
                        $output['columns'][] = "$tablealias.$column";
                    }
                }
            }
        }
        return $output;
    }
    
    /**
     * Run the query.
     * @return Rowset
     */
    public function query() {
        return $this->driver->query($this->__toString());
    }
}
?>